突然很好奇 devise 如何在 rails console 裡面隱藏 columns, 例如在 schema 裡面 users 這個 table 明明有 encrypted_password 這個欄位為什麼 console 不會出現呢?
ActiveRecord::Relation#inspect
首先了解我們在 console 撈 ActiveRecord 為什麼會回傳以下的東西1
2$ User.first
#<User id: 1, email: "test@test.com", created_at: "2020-05-08 09:18:36", updated_at: "2020-05-08 09:18:36">
原來是在 ActiveRecord::Relation#inspect 這個 method 做事情1
2
3
4
5
6
7
8def inspect
  subject = loaded? ? records : self
  entries = subject.take([limit_value, 11].compact.min).map!(&:inspect)
  entries[10] = "..." if entries.size == 11
  "#<#{self.class.name} [#{entries.join(', ')}]>"
end
而 devise 這個 gem 在 module Authenticatable 定義如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38BLACKLIST_FOR_SERIALIZATION = [
  :encrypted_password,:reset_password_token, :reset_password_sent_at,
  :remember_created_at,
  :sign_in_count,
  :current_sign_in_at,
  :last_sign_in_at,
  :current_sign_in_ip,
  :last_sign_in_ip,
  :password_salt,
  :confirmation_token,
  :confirmed_at,
  :confirmation_sent_at,
  :remember_token,
  :unconfirmed_email,
  :failed_attempts,
  :unlock_token,
  :locked_at
]
def serializable_hash(options = nil)
  options = options.try(:dup) || {}
  options[:except] = Array(options[:except])
  if options[:force_except]
    options[:except].concat Array(options[:force_except])
  else
    options[:except].concat BLACKLIST_FOR_SERIALIZATION
  end
  super(options)
end
def inspect
  inspection = serializable_hash.collect do |k,v|
    "#{k}: #{respond_to?(:attribute_for_inspect) ? attribute_for_inspect(k) : v.inspect}"
  end
  "#<#{self.class} #{inspection.join(", ")}>"
end
當然可以在任一 model 重新定義他, 即使隨意修改另一個字串也依然可以對 User.last 這個 ActiveRecord object 正常操作, 這個只是在撈資料的時候回傳一個好讀的字串而已, 官方的解釋如下:Returns the contents of the record as a nicely formatted string.
那如何看到全部 column 以及 value 呢
簡單的下 attributes methods 即可:1
2$ User.last.attributes
{"id"=>1, "email"=>"test@test.com", "encrypted_password"=>"xxxxx", "reset_password_token"=>nil, "reset_password_sent_at"=>nil, "remember_created_at"=>nil, "confirmation_token"=>"xxxxx", "confirmed_at"=>nil, "confirmation_sent_at"=>Fri, 08 May 2020 09:18:36 UTC +00:00, "unconfirmed_email"=>nil, "created_at"=>Fri, 08 May 2020 09:18:36 UTC +00:00, "updated_at"=>Fri, 08 May 2020 09:18:36 UTC +00:00}
只是要注意這個就是一個 hash 嘍!
只想在某一個 API 像這樣 hide 一些欄位呢?
我們可以用去除法(except)1
User.first.serializable_hash({except: [:email, :id]}).to_json
只想取某幾個欄位的方法(only)1
User.first.serializable_hash({only: [:email, :id]}).to_json
甚至可以取 nested 的欄位1
2# user has_many notes
User.first.serializable_hash(include: { notes: { only: 'title' }})
參考文章
Rails官方 for inspect
Rails官方 for serializable_hash
Stackoverflow - Rails 5 model objects not showing all Devise attributes
Stackoverflow - Rails & Devise: devise specific columns not showing up in rails console